//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include "cinterpreter.h"
#include <buola/python.h>
#include <buola/python/parser.h>
#include <buola/python/types.h>
#include <buola/io/curi.h>
#include <buola/algorithm/comparison.h>
#include <fcntl.h>
#include <buola/io/cfolder.h>
#include <buola/io/cresourcefinder.h>
#include <buola/os/cenv.h>
#include <buola/os/path.h>
#include <buola/os/user.h>

#include "builtins/caliasbuiltin.h"
#include "builtins/cbgbuiltin.h"
#include "builtins/cbuiltinsbuiltin.h"
#include "builtins/ccdbuiltin.h"
#include "builtins/cexportbuiltin.h"
#include "builtins/cfgbuiltin.h"
#include "builtins/cjobsbuiltin.h"
#include "builtins/ckilljobbuiltin.h"
#include "builtins/cpythonbuiltin.h"
#include "builtins/csourcebuiltin.h"
#include "builtins/cunaliasbuiltin.h"
#include "builtins/cunexportbuiltin.h"

namespace buola { namespace bush {

CInterpreter *gInterpreter;
    
PyObject *shell_method(PyObject* /*pSelf*/,PyObject *pObject)
{
    python::YObject lObject(pObject);
    
    if(!lObject.IsString())
    {
        PyErr_SetString(PyExc_RuntimeError,"shell method takes a string");
        return nullptr;
    }
    
    std::wstring lCode=lObject.ToWString();

    if(lCode.empty())
        Py_RETURN_NONE;
    
    try
    {
        int lReturn=gInterpreter->RunShell(lCode);
        return python::YObject(lReturn).IncRef();
    }
    catch(std::exception &pX)
    {
        PyErr_SetString(PyExc_RuntimeError,pX.what());
        return python::YObject();
    }
}

PyObject *shellout_method(PyObject* /*pSelf*/,PyObject *pObject)
{
    python::YObject lObject(pObject);

    if(!lObject.IsString())
    {
        PyErr_SetString(PyExc_RuntimeError,"shell method takes a string");
        return nullptr;
    }
    
    std::wstring lCode=lObject.ToWString();
    
    if(lCode.empty())
    {
        return python::YObject(L"").IncRef();
    }

    try
    {
        std::wstring lReturn=utf32(gInterpreter->RunShellOut(lCode));
        return python::YObject(lReturn).IncRef();
    }
    catch(std::exception &pX)
    {
        PyErr_SetString(PyExc_RuntimeError,pX.what());
        return python::YObject();
    }
}

PyObject *run_method(PyObject* /*pSelf*/,PyObject *pString)
{
    python::YObject lString(pString);
    
    if(lString.IsString())
    {
        gInterpreter->EvalString(lString.ToWString(),PyEval_GetGlobals(),PyEval_GetLocals());
    }
    else
    {
        PyErr_BadArgument();
        return python::YObject();
    }

    Py_RETURN_NONE;
}

PyObject *shellsource_method(PyObject* /*pSelf*/,PyObject *pFile)
{
    python::YObject lFile(pFile);
    
    if(lFile.IsString())
    {
        gInterpreter->SourceFile(io::file(lFile.ToString()),PyEval_GetGlobals(),PyEval_GetLocals());
    }
    else
    {
        PyErr_BadArgument();
        return python::YObject();
    }

    Py_RETURN_NONE;
}

PyObject *addcompleter_method(PyObject* /*pSelf*/,PyObject *pArgs)
{
    python::YObject lString;
    python::YCallable lFunction;
    
    if(!python::parse_args(pArgs,"OO",lString,lFunction))
        return nullptr;
    
    if(!lString.IsString()||!lFunction.IsCallable())
    {
        PyErr_BadArgument();
        return nullptr;
    }
    
    gInterpreter->AddCompleter(lString.ToString(),lFunction);
    
    Py_RETURN_NONE;
}

PyObject *removecompleter_method(PyObject* /*pSelf*/,PyObject *pString)
{
    python::YObject lString(pString);
    
    if(!lString.IsString())
    {
        PyErr_BadArgument();
        return nullptr;
    }
    
    gInterpreter->RemoveCompleter(lString.ToString());
    
    Py_RETURN_NONE;
}

PyObject *addbuiltin_method(PyObject* /*pSelf*/,PyObject *pArgs)
{
    python::YObject lString;
    python::YCallable lFunction;
    python::YObject lDescription;
    
    if(!python::parse_args(pArgs,"OOO",lString,lFunction,lDescription))
        return nullptr;
    
    if(!lString.IsString()||!lFunction.IsCallable()||!lDescription.IsString())
    {
        PyErr_BadArgument();
        return nullptr;
    }
    
    CPythonBuiltin *lBuiltin=new CPythonBuiltin(lFunction,lDescription.ToWString());
    
    gInterpreter->AddBuiltin(lString.ToString(),lBuiltin);
    
    Py_RETURN_NONE;
}

PyObject *removebuiltin_method(PyObject* /*pSelf*/,PyObject *pString)
{
    python::YObject lString(pString);
    
    if(!lString.IsString())
    {
        PyErr_BadArgument();
        return nullptr;
    }
    
    gInterpreter->RemoveBuiltin(lString.ToString());
    
    Py_RETURN_NONE;
}

python::YTuple group_to_tuple(const tty::CCompletion<std::wstring>::SGroup &pGroup)
{
    python::YTuple lOptions(pGroup.mOptions.size());
    
    for(int i=0;i<pGroup.mOptions.size();i++)
    {
        python::YTuple lOption(3);
        lOption.Set(0,python::YObject(pGroup.mOptions[i].mText));
        lOption.Set(1,python::YObject(pGroup.mOptions[i].mDisplay));
        lOption.Set(2,python::YObject((int32_t)pGroup.mOptions[i].mColor));
        lOptions.Set(i,lOption);
    }
    
    python::YTuple lTuple(2);
    lTuple.Set(0,python::YObject(pGroup.mName));
    lTuple.Set(1,lOptions);

    lTuple.IncRef();
    return lTuple;
}

PyObject *completefiles_method(PyObject* /*pSelf*/,PyObject *pArgs)
{
    python::YObject lString;
    python::YSeq lExtensions;
    
    if(!python::parse_args(pArgs,"O|O",lString,lExtensions))
        return nullptr;
    
    if(!lString.IsString()||(!lExtensions.IsNull()&&!lExtensions.IsSequence()))
    {
        PyErr_BadArgument();
        return nullptr;
    }
    
    tty::CCompletion<std::wstring>::SGroup lGroup;
    lGroup.mName=L"Files";

    if(lExtensions.IsNull())
    {
        gInterpreter->CompleteFilenames("",lString.ToString(),lGroup);
    }
    else
    {
        std::vector<std::string> lExt;
        for(int i=0;i<lExtensions.size();i++)
        {
            if(lExtensions[i].IsString())
            {
                lExt.push_back(lExtensions[i].ToString());
            }
        }

        gInterpreter->CompleteFilenames("",lString.ToString(),lGroup,lExt);
    }
    
    return group_to_tuple(lGroup).IncRef();
}

PyObject *completecommand_method(PyObject *pSelf,PyObject *pArgs)
{
    python::YSeq lSeq;
    python::YObject lExtra;
    
    if(!python::parse_args(pArgs,"O|O",lSeq,lExtra))
        return nullptr;

    if(!lSeq.IsSequence())
    {
        PyErr_BadArgument();
        return nullptr;
    }
    
    if(!lSeq.size()) return nullptr;
    
    if(lSeq.size()==1)
    {
        tty::CCompletion<std::wstring>::SGroup lGroup;
        lGroup.mName=L"Command";
        gInterpreter->CompleteCommand(lSeq[0].ToString(),lGroup);
        python::YTuple lTuple(1,group_to_tuple(lGroup));
        return lTuple.IncRef();
    }
    else
    {
        python::YCallable lCompleter=gInterpreter->GetCompleter(lSeq[0].ToString());
        
        if(lCompleter)
        {
            if(lExtra.IsNull())
                lExtra=python::YTuple();
            return lCompleter.Call(lSeq,lExtra).IncRef();
        }
        else
        {
            python::YTuple lLast(1,lSeq[lSeq.size()-1]);
            python::YTuple lTuple(1,python::YObject(completefiles_method(pSelf,lLast),false));
            return lTuple.IncRef();
        }
    }
}

PyObject *shellglob_method(PyObject* /*pSelf*/,PyObject *pObject)
{
    python::YObject lObject(pObject);
    
    if(!lObject.IsString())
    {
        PyErr_SetString(PyExc_RuntimeError,"shellglob method takes a string");
        return nullptr;
    }
    
    std::string lGlobExpr=lObject.ToString();
    python::YList lReturn;

    if(lGlobExpr.empty())
        return lReturn.IncRef();
    
    try
    {
        std::vector<std::string> lResult;
        
        io::glob(lGlobExpr,io::GLOB_BRACES|io::GLOB_TILDES|io::GLOB_KEEPTILDE|io::GLOB_SORT,lResult);
        
        for(int i=0;i<lResult.size();i++)
        {
            lReturn.Append(python::YObject(lResult[i]));
        }

        return lReturn.IncRef();
    }
    catch(std::exception &pX)
    {
        PyErr_SetString(PyExc_RuntimeError,pX.what());
        return nullptr;
    }
}

PyMethodDef lShellMethodDef={"shell",shell_method,METH_O,nullptr};
PyMethodDef lShellOutMethodDef={"shellout",shellout_method,METH_O,nullptr};
PyMethodDef lRunMethodDef={"run",run_method,METH_O,nullptr};
PyMethodDef lShellSourceMethodDef={"shellsource",shellsource_method,METH_O,nullptr};
PyMethodDef lAddCompleterMethodDef={"addcompleter",addcompleter_method,METH_VARARGS,nullptr};
PyMethodDef lRemoveCompleterMethodDef={"removecompleter",removecompleter_method,METH_O,nullptr};
PyMethodDef lAddBuiltinMethodDef={"addbuiltin",addbuiltin_method,METH_VARARGS,nullptr};
PyMethodDef lRemoveBuiltinMethodDef={"removebuiltin",removebuiltin_method,METH_O,nullptr};
PyMethodDef lCompleteFilesMethodDef={"completefiles",completefiles_method,METH_VARARGS,nullptr};
PyMethodDef lCompleteCommandMethodDef={"completecommand",completecommand_method,METH_VARARGS,nullptr};
PyMethodDef lShellGlobMethodDef={"shellglob",shellglob_method,METH_O,nullptr};
    
CInterpreter::CInterpreter()
    :   mPrompt(L"%u # ")
    ,   mState(STATE_EMPTY)
    ,   mCompletersLoaded(false)
    ,   mPendingJobReport(false)
{
    python::python_init();

    python::YModule lBuiltins=python::get_builtins_module();
    python::YCFunction lShellFunction(&lShellMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("shell",lShellFunction);
    python::YCFunction lShellOutFunction(&lShellOutMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("shellout",lShellOutFunction);
    python::YCFunction lRunFunction(&lRunMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("run",lRunFunction);
    python::YCFunction lShellSourceFunction(&lShellSourceMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("shellsource",lShellSourceFunction);
    python::YCFunction lAddCompleterFunction(&lAddCompleterMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("addcompleter",lAddCompleterFunction);
    python::YCFunction lRemoveCompleterFunction(&lRemoveCompleterMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("removecompleter",lRemoveCompleterFunction);
    python::YCFunction lAddBuiltinFunction(&lAddBuiltinMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("addbuiltin",lAddBuiltinFunction);
    python::YCFunction lRemoveBuiltinFunction(&lRemoveBuiltinMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("removebuiltin",lRemoveBuiltinFunction);
    python::YCFunction lCompleteFilesFunction(&lCompleteFilesMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("completefiles",lCompleteFilesFunction);
    python::YCFunction lCompleteCommandFunction(&lCompleteCommandMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("completecommand",lCompleteCommandFunction);
    python::YCFunction lShellGlobFunction(&lShellGlobMethodDef,lBuiltins,lBuiltins);
    lBuiltins.AddObject("shellglob",lShellGlobFunction);

    python::YModule lOS=python::import_module("os");
    lBuiltins.AddObject("environ",lOS.GetAttribute("environ"));

    AddBuiltin("alias",new CAliasBuiltin);
    AddBuiltin("bg",new CBgBuiltin);
    AddBuiltin("builtins",new CBuiltinsBuiltin);
    AddBuiltin("cd",new CCdBuiltin);
    AddBuiltin("export",new CExportBuiltin);
    AddBuiltin("fg",new CFgBuiltin);
    AddBuiltin("jobs",new CJobsBuiltin);
    AddBuiltin("killjob",new CKillJobBuiltin);
    AddBuiltin("source",new CSourceBuiltin);
    AddBuiltin("unalias",new CUnaliasBuiltin);
    AddBuiltin("unexport",new CUnexportBuiltin);
//    mBuiltinMap["exit"]=BUILTIN_EXIT;

    //initialize environment and CD stack
    io::CURI lWD=io::get_cwd();
    mCDStack.push_back(lWD);
    CEnv::Set("PWD",lWD.Path());
}

void CInterpreter::SetPrompt(const std::wstring &pPrompt)
{
    mPrompt=pPrompt;
}

std::wstring CInterpreter::GetPrompt()
{
    if(mState==STATE_EMPTY)
    {
        const std::wstring &lStr=mPrompt;
        
        std::wstring lOut;
        
        for(int c=0;c<lStr.length();++c)
        {
            if(lStr[c]=='%')
            {
                if(++c>=lStr.length()) break;
                
                switch(lStr[c])
                {
                case L'h':
                    {
                        std::string lHost=get_host_name();
                        lOut+=utf32(lHost.substr(0,lHost.find('.')));
                    }
                    break;
                case L'w':
                    lOut+=utf32(mCDStack.front().Path());
                    break;
                case L'u':
                    lOut+=utf32(os::get_user().Name());
                    break;
                case L'W':
                    {
                        ///\todo use some path manipulation function instead
                        std::string lCWD=mCDStack.front().Path();
                        std::string lHome=io::get_home().Path();
                        if(lCWD==lHome||starts_with(lCWD+'/',lHome+'/'))
                        {
                            lCWD="~"+lCWD.substr(lHome.length());
                        }
                        lOut+=utf32(lCWD);
                    }
                    break;
                case L'[':
                case L']':
                    lOut+=L'\037';
                    break;
                case L'{': //long command
                    {
                        std::size_t e=lStr.find(L'}',c+1);
                        
                        if(e!=std::wstring::npos)
                        {
                            std::wstring lText=lStr.substr(c+1,e-(c+1));

                            if(lText==L"blue")
                                lOut+=L"\037\e[1;34m\037";
                            else if(lText==L"yellow")
                                lOut+=L"\037\e[1;33m\037";
                            else if(lText==L"normal")
                                lOut+=L"\037\e[m\037";
                            
                            c=e;
                        }
                    }                
                    break;
                case L'%':
                    lOut+=lStr[c];
                default:
                    break;
                }
            }
            else
                lOut+=lStr[c];
        }
        
        mLastPrompt=lOut;
        return lOut;
    }
    else
    {
        int lPrintable=1;
        int lLength=0;
        for(int i=0;i<mLastPrompt.size();i++)
        {
            if(mLastPrompt[i]=='\037')
                lPrintable=1-lPrintable;
            else
                lLength+=lPrintable;
        }
        
        if(lLength>6)
        {
            std::wstring lOut(lLength-6,L' ');
            lOut+=L"... > ";
            return lOut;
        }
        else if(lLength>2)
        {
            std::wstring lOut(lLength-2,L' ');
            lOut+=L"> ";
            return lOut;
        }
        return L"> ";
    }
}

std::string CInterpreter::GetWindowTitle()
{
    std::string lCWD=io::get_cwd().Path();
    std::string lHome=io::get_home().Path();
    if(starts_with(lCWD,lHome))
    {
        lCWD="~"+lCWD.substr(lHome.length());
    }
    return os::get_user().Name()+'@'+get_host_name()+": "+lCWD;
}

void CInterpreter::CancelLine()
{
    mAccumLine.clear();
    mIndentLevels.clear();
    mState=STATE_EMPTY;
}

void CInterpreter::FeedLine(std::wstring& pLine)
{
    if(mAccumLine.empty()&&pLine.empty()) return;

    mAccumLine+=pLine;
    mAccumLine+='\n';
    
    mState=RunCode(mAccumLine,mIndentLevels,python::get_main_module().GetDict(),python::get_main_module().GetDict(),
                    RUN_SINGLE);
    
    if(mState==STATE_EMPTY)
    {
        mAccumLine.clear();
        mIndentLevels.clear();
    }
    
    //adjust the line for history
    int lEnd=pLine.size();
    while(lEnd>0&&pLine[lEnd-1]==' ')
        lEnd--;
    if(lEnd!=pLine.size())
    {
        int lSlashes=lEnd;
        while(lSlashes>0&&pLine[lSlashes-1]=='\\')
            lSlashes--;
        if((lEnd-lSlashes)%2) //odd number of slashes, keep one space
            lEnd++;
        pLine.resize(lEnd);
    }
}

int CInterpreter::RunShell(const std::wstring& pString)
{
    std::vector<CToken> lTokens;
    PPipeline lPipeline=mParser.ParseShellLine(pString,lTokens);
    
    if(!lPipeline) throw XData("syntax error in shell expression");

    return lPipeline->Launch(true);
}

std::string CInterpreter::RunShellOut(const std::wstring& pString)
{
    std::vector<CToken> lTokens;
    PPipeline lPipeline=mParser.ParseShellLine(pString,lTokens);
    
    if(!lPipeline) throw XData("syntax error in shell expression");

    std::string lOutput;
    lPipeline->SetOutput(&lOutput);
    lPipeline->Launch(false);
    return lOutput;
}

python::YObject CInterpreter::RunParsedLine(const std::wstring &pParsedLine,const python::YObject &pGlobals,
                                 const python::YObject &pLocals,ERunMode pRunMode,bool pIgnoreError)
{
    python::EParseType lType;
    
    switch(pRunMode)
    {
    case RUN_EXPRESSION:
        lType=python::PARSE_EXPRESSION;
        break;
    case RUN_FILE:
        lType=python::PARSE_FILE;
        break;
    case RUN_SINGLE:
    default:
        lType=python::PARSE_LINE;
        break;
    }

    python::YCode lCode=python::compile_line(utf8(pParsedLine),lType);
    
    if(!lCode)
    {
        msg_info() << "compilation error!\n";
        python::error_print();
    }
    else
    {
        python::YObject lResult=lCode.Evaluate(pGlobals,pLocals);

        if(python::error_occurred())
        {
            if(pIgnoreError)
                python::error_clear();
            else
                python::error_print();
        }
        
        return lResult;
    }
    
    return python::YObject();
}

python::YObject CInterpreter::RunParsedLineModule(const std::wstring &pParsedLine,const std::string &pModule,
                                 ERunMode pRunMode,bool pIgnoreError)
{
    python::EParseType lType;
    
    switch(pRunMode)
    {
    case RUN_EXPRESSION:
        lType=python::PARSE_EXPRESSION;
        break;
    case RUN_FILE:
        lType=python::PARSE_FILE;
        break;
    case RUN_SINGLE:
    default:
        lType=python::PARSE_LINE;
        break;
    }

    python::YCode lCode=python::compile_line(utf8(pParsedLine),lType);
    
    if(!lCode)
    {
        msg_info() << "compilation error!\n";
        python::error_print();
    }
    else
    {
        python::YObject lResult=lCode.ExecModule(pModule);

        if(python::error_occurred())
        {
            if(pIgnoreError)
                python::error_clear();
            else
                python::error_print();
        }
        
        return lResult;
    }
    
    return python::YObject();
}

CInterpreter::EState CInterpreter::RunCode(const std::wstring &pLine,std::vector<int> &pIndentLevels,
                                           const python::YObject &pGlobals,const python::YObject &pLocals,
                                           ERunMode pRunMode)
{
    std::wstring lParsedLine;
    EParseResult lResult=mParser.ParseLine(pLine,lParsedLine,pIndentLevels,pRunMode);
    
    if(lResult==PARSE_OK)
    {
        RunParsedLine(lParsedLine,pGlobals,pLocals,pRunMode);
        return STATE_EMPTY;
    }
    else if(lResult==PARSE_INCOMPLETE)
    {
        return STATE_CONTINUE;
    }
    else if(lResult==PARSE_EMPTY)
    {
        return STATE_EMPTY;
    }

    msg_info() << "error!!!\n";

    return STATE_EMPTY;
}

CInterpreter::EState CInterpreter::RunCodeModule(const std::wstring &pLine,std::vector<int> &pIndentLevels,
                                           const std::string &pModule,ERunMode pRunMode)
{
    std::wstring lParsedLine;
    EParseResult lResult=mParser.ParseLine(pLine,lParsedLine,pIndentLevels,pRunMode);
    
    if(lResult==PARSE_OK)
    {
        RunParsedLineModule(lParsedLine,pModule,pRunMode);
        return STATE_EMPTY;
    }
    else if(lResult==PARSE_INCOMPLETE)
    {
        return STATE_CONTINUE;
    }
    else if(lResult==PARSE_EMPTY)
    {
        return STATE_EMPTY;
    }

    msg_info() << "error!!!\n";

    return STATE_EMPTY;
}

void CInterpreter::EvalString(const std::wstring &pString,const python::YObject &pGlobals,const python::YObject &pLocals)
{
    std::wstring lAccum;
    
    auto lIt=pString.begin();
    while(lIt!=pString.end())
    {
        auto lB=lIt;
        lIt=std::find(lB,pString.end(),L'\n');
        if(lIt==lB&&lAccum.empty())
        {
            ++lIt;
            continue;
        }
        if(lIt==pString.end())
        {
            lAccum.append(lB,lIt);
            lAccum+=L'\n';
        }
        else
        {
            lAccum.append(lB,++lIt);
        }
        std::vector<int> lIndentLevels;
        if(RunCode(lAccum,lIndentLevels,pGlobals,pLocals,RUN_SINGLE)==STATE_EMPTY)
            lAccum.clear();
    }
}

std::string CInterpreter::EvalIdentifier(TTokenRange pExpression)
{
    std::wstring lIdentifier(pExpression.first->mB+1,pExpression.first->mE);
    
    python::YObject lObject=RunParsedLine(lIdentifier,PyEval_GetGlobals(),PyEval_GetLocals(),RUN_EXPRESSION,true);
    
    if(lObject) return lObject.GetStr();
    
    return CEnv::Get(utf8(lIdentifier));
}

std::string CInterpreter::EvalExpression(TTokenRange pExpression)
{
    if(pExpression.first==pExpression.second) return std::string();
    std::wstring lPythonExpr;
    
    for(auto lT=pExpression.first+1;lT!=pExpression.second-1;++lT)
        lPythonExpr.append(lT->mB,lT->mE);
    
    python::YObject lObject=RunParsedLine(lPythonExpr,PyEval_GetGlobals(),PyEval_GetLocals(),RUN_EXPRESSION);
    
    if(!lObject) return std::string();
    return lObject.GetStr();
}

std::vector<std::string> CInterpreter::EvalExpressionToList(TTokenRange pExpression)
{
    std::vector<std::string> lResult;

    if(pExpression.first==pExpression.second) return lResult;
    std::wstring lPythonExpr;
    
    for(auto lT=pExpression.first+1;lT!=pExpression.second-1;++lT)
        lPythonExpr.append(lT->mB,lT->mE);
    
    python::YObject lObject=RunParsedLine(lPythonExpr,PyEval_GetGlobals(),PyEval_GetLocals(),RUN_EXPRESSION);
    
    if(lObject)
    {
        if(lObject.IsSequence())
        {
            python::YSeq lSeq(lObject);
            for(int i=0;i<lSeq.size();i++)
            {
                lResult.push_back(lSeq[i].GetStr());
            }
        }
        else
        {
            lResult.push_back(lObject.GetStr());
        }
    }
    
    return lResult;
}

void CInterpreter::SourceFile(const io::CURI &pURI,const python::YObject &pGlobals,const python::YObject &pLocals)
{
    io::PBufferStream lStream=io::open_buffered(pURI,io::EMode::READ);
    std::wstring lBuffer;
    std::wstring lAccum;
    
    while(get_line(lStream,lBuffer))
        lAccum+=lBuffer+L'\n';
        
    std::vector<int> lIndentLevels;
    RunCode(lAccum,lIndentLevels,pGlobals,pLocals,RUN_FILE);
}

void CInterpreter::SourceFileModule(const io::CURI &pURI,const std::string &pModule)
{
    io::PBufferStream lStream=io::open_buffered(pURI,io::EMode::READ);
    std::wstring lBuffer;
    std::wstring lAccum;
    
    while(get_line(lStream,lBuffer))
        lAccum+=lBuffer+L'\n';
        
    std::vector<int> lIndentLevels;
    RunCodeModule(lAccum,lIndentLevels,pModule,RUN_FILE);
}

bool CInterpreter::AddAlias(const std::string &pDecl)
{
    std::size_t lPos=pDecl.find_first_of(" |&;()<>\'\"=");
    
    if(lPos==std::string::npos)
    {
        msg_error() << "alias declaration must contain a =\n";
        return false;
    }
    
    if(pDecl[lPos]!='='||lPos==0)
    {
        msg_error() << "alias name can't contain spaces or special characters\n";
        return false;
    }

    CAlias *lAlias=new CAlias;
    lAlias->mExpansion=utf32(pDecl.substr(lPos+1));
    
    if(!mParser.ParseAlias(*lAlias))
    {
        msg_error() << "alias expansion must be a valid shell command\n";
        return false;
    }

    std::wstring lName=utf32(pDecl.substr(0,lPos));

    mAliases[lName]=lAlias;

    return true;    
}

bool CInterpreter::RemoveAlias(const std::string &pName)
{
    std::wstring lName=utf32(pName);

    auto lI=mAliases.find(lName);
    if(lI==mAliases.end())
        return false;
    delete lI->second;
    mAliases.erase(lI);
    return true;
}

void CInterpreter::AddBuiltin(const std::string& pCommand,CBuiltin *pBuiltin)
{
    mBuiltins[pCommand]=pBuiltin;
}

void CInterpreter::RemoveBuiltin(const std::string& pCommand)
{
    auto i=mBuiltins.find(pCommand);
    if(i==mBuiltins.end())
    {
        msg_error() << "builtin not found\n";
        return;
    }
    else if(!i->second->IsRemovable())
    {
        msg_error() << "can't remove non-custom builtin\n";
        return;
    }
    
    delete i->second;
    
    mBuiltins.erase(pCommand);
}

CBuiltin *CInterpreter::GetBuiltin(const std::string &pCommand)
{
    auto lFindBuiltin=mBuiltins.find(pCommand);
        
    if(lFindBuiltin!=mBuiltins.end())
        return lFindBuiltin->second;

    return nullptr;
}

void CInterpreter::AddCompleter(const std::string& pCommand,const python::YCallable& pCallable)
{
    mCompleters[pCommand]=pCallable;
}

void CInterpreter::RemoveCompleter(const std::string& pCommand)
{
    mCompleters.erase(pCommand);
}

python::YCallable CInterpreter::GetCompleter(const std::string& pCmd)
{
    auto lFindComp=mCompleters.find(pCmd);
        
    if(lFindComp!=mCompleters.end())
        return lFindComp->second;
    
    //here we should do something related to loading all the available completers
    if(!mCompletersLoaded)
    {
        std::vector<io::CURI> lList=io::data_finder().FindFolders("buola/bush/completion.d");
        for(auto i=lList.rbegin();i!=lList.rend();++i)
        {
            io::CFolder lFolder(*i);
            
            std::vector<std::string> lNames=lFolder.GetChildrenNames();
            
            for(int j=0;j<lNames.size();j++)
                mCompletersAvailable[lNames[j]]=*i/lNames[j];
        }
        mCompletersLoaded=true;
    }

    auto lFindAvailable=mCompletersAvailable.find(pCmd);
    
    if(lFindAvailable!=mCompletersAvailable.end())
    {
        io::CURI lURI=lFindAvailable->second;
        mCompletersAvailable.erase(lFindAvailable);
        SourceFileModule(lURI,"bushcompleter_"+pCmd);
    }

    lFindComp=mCompleters.find(pCmd);
        
    if(lFindComp!=mCompleters.end())
        return lFindComp->second;

    return nullptr;
}

bool CInterpreter::Complete(const std::wstring &pLine,int pCursor,tty::CCompletion<std::wstring> &pComp)
{
    std::wstring lJoin=mAccumLine+pLine;
    
    CPartialMatch lMatch;
    mParser.ParsePartial(lJoin.begin(),lJoin.begin()+mAccumLine.length()+pCursor,lMatch);
    
    if(lMatch.mContext==COMP_NONE) return false;
    
    pComp.mB=lMatch.mWordB-lJoin.begin()-mAccumLine.size();
    pComp.mE=lMatch.mWordE-lJoin.begin()-mAccumLine.size();

    if(lMatch.mContext&COMP_SHELL)
    {
        if(CompleteShell(lMatch,pComp))
            return true;
    }
    if(lMatch.mContext&COMP_PYTHON_ANY)
    {
        CompletePython(lMatch,pComp);
    }
    
    return true;
}

bool CInterpreter::CompleteShell(CPartialMatch &pMatch,tty::CCompletion<std::wstring> &pComp)
{
    std::vector<CWord> lWords;
    
    for(auto i=pMatch.mWords.begin();i!=pMatch.mWords.end();++i)
    {
        CWord lWord;
        lWord.AppendTokens(*i);
        lWords.push_back(std::move(lWord));
    }
    
    if(lWords.size()==1)
    {
        CWord &lW=lWords.front();
        
        tty::CCompletion<std::wstring>::SGroup lGroup;
        
        CompleteCommand(lW.mChars,lGroup);
        if(!lGroup.mOptions.empty())
        {
            pComp.mGroups.push_back(lGroup);
            //if it contains a / and it matches files, it's almost for sure not python, so return true
            if(lW.mChars.find('/')!=std::string::npos)
                return true;
        }
    }
    else
    {
        CWord &lCmd=lWords.front();
        
        python::YCallable lCompleter=GetCompleter(lCmd.mChars);
        
        if(lCompleter)
        {
            python::YTuple lTuple(lWords.size());
            for(int i=0;i<lWords.size();i++)
                lTuple.Set(i,python::YObject(lWords[i].mChars));
            python::YSeq lGroups(lCompleter.Call(lTuple,python::YTuple()));
            
            if(lGroups.IsNull()) return false;
            if(!lGroups.IsSequence()) return false;
            
            for(int i=0;i<lGroups.size();i++)
            {
                python::YSeq lG(lGroups[i]);
                if(!lG.IsSequence()||lG.size()<2) continue;
                if(!lG[0].IsString()) continue;
                if(!lG[1].IsSequence()) continue;
                
                tty::CCompletion<std::wstring>::SGroup lGroup;
                lGroup.mName=lG[0].ToWString();
                lGroup.mSorted=false;
                if(lG.size()>=3)
                {
                    if(!lG[2].IsBool()) continue;
                    lGroup.mSorted=lG[2].ToBool();
                }
                
                python::YSeq lOptions(lG[1]);

                for(int j=0;j<lOptions.size();j++)
                {
                    python::YObject lO(lOptions[j]);
                    if(lO.IsString())
                    {
                        std::wstring lName=lO.ToWString();
                        lGroup.mOptions.emplace_back(lName,lName,tty::COLOR_NORMAL);
                    }
                    else if(lO.IsSequence())
                    {
                        python::YSeq lOS(lO);
                        if(lOS.size()<1||lOS.size()>3) continue;
                        
                        if(!lOS[0].IsString()) continue;
                        std::wstring lName=lOS[0].ToWString();
                        std::wstring lDisplay=lName;
                        tty::EColor lColor=tty::COLOR_NORMAL;
                        
                        if(lOS.size()==2)
                        {
                            if(lOS[1].IsString())
                                lDisplay=lOS[1].ToWString();
                            else if(lOS[1].IsInt())
                            {
                                int lValue=lOS[1].ToInt();
                                if(lValue>=0&&lValue<=15)
                                    lColor=(tty::EColor)lValue;
                            }
                            else
                                continue;
                        }
                        else if(lOS.size()==3)
                        {
                            if(!lOS[1].IsString()) continue;
                            if(!lOS[2].IsInt()) continue;

                            lDisplay=lOS[1].ToWString();
                            int lValue=lOS[2].ToInt();
                            if(lValue>=0&&lValue<=15)
                                lColor=(tty::EColor)lValue;
                        }
                        
                        lGroup.mOptions.emplace_back(lName,lDisplay,lColor);
                    }
                }                

                if(!lGroup.mOptions.empty())
                    pComp.mGroups.push_back(std::move(lGroup));
            }
        }
        else
        {
            CWord &lW=lWords.back();
        
            if(lW.mFormat&CWord::FORMAT_GLOB) return false; //ignore if itself is a glob

            tty::CCompletion<std::wstring>::SGroup lGroup;

            if(lWords[0].mChars=="cd")
            {
                lGroup.mName=L"Folders";

                CompleteFilenames("",lW.mChars,lGroup,io::GLOB_ONLYDIR);
            }
            else
            {
                lGroup.mName=L"Filenames";

                CompleteFilenames("",lW.mChars,lGroup);
            }
        
            lGroup.mSorted=false;
        
            if(!lGroup.mOptions.empty())
                pComp.mGroups.push_back(std::move(lGroup));
        }
    }
    
    return false;
}

static void fill_group_with_list(const python::YList &pList,const std::wstring &pPart,const std::wstring &pAdd,
                                 tty::CCompletion<std::wstring>::SGroup &pGroup)
{
    if(pList.IsNull()) return;
    for(int i=0;i<pList.size();i++)
    {
        std::wstring lName=pList[i].ToWString();
        if(!starts_with(lName,pPart)) continue;
        pGroup.mOptions.emplace_back(pAdd+lName,lName,tty::COLOR_WHITE);
    }
}

void CInterpreter::CompletePython(CPartialMatch &pMatch,tty::CCompletion<std::wstring> &pComp)
{
    if(pMatch.mContext&(COMP_COMPKW|COMP_SIMPLEKW))
    {
        tty::CCompletion<std::wstring>::SGroup lGroup;
        lGroup.mName=L"Keywords";
        
        auto &lTable=mParser.GetTokenizer().Keywords();
        std::vector<std::wstring> lValues;
        
        int lType;
        if(pMatch.mContext&COMP_COMPKW&&pMatch.mContext&COMP_SIMPLEKW)
            lType=TOKENTYPE_COMPKW|TOKENTYPE_SIMPLEKW;
        else if(pMatch.mContext&COMP_COMPKW)
            lType=TOKENTYPE_COMPKW;
        else
            lType=TOKENTYPE_SIMPLEKW;
            
        lTable.FindAllKeysIf(pMatch.mWordB,pMatch.mWordE,lValues,[lType](EToken pT)->bool{return pT&lType;});

        for(int i=0;i<lValues.size();i++)
        {
            lGroup.mOptions.emplace_back(pMatch.mPythonAdd+lValues[i],lValues[i],tty::COLOR_BRIGHTWHITE);
        }

        if(!lGroup.mOptions.empty())
            pComp.mGroups.push_back(std::move(lGroup));
    }
    if(pMatch.mContext&(COMP_ATOM|COMP_GLOBAL))
    {
        tty::CCompletion<std::wstring>::SGroup lGroup;
        lGroup.mName=L"Python";
        
        fill_group_with_list(python::get_main_module().GetDict().GetKeys(),pMatch.mPythonPart,pMatch.mPythonAdd,lGroup);
        if(pMatch.mContext&COMP_ATOM)
            fill_group_with_list(python::get_builtins_module().GetDict().GetKeys(),pMatch.mPythonPart,pMatch.mPythonAdd,lGroup);

        if(!lGroup.mOptions.empty())
            pComp.mGroups.push_back(std::move(lGroup));
    }
    if(pMatch.mContext&COMP_AFTERDOT)
    {
        bool lLastDot=false;
        std::vector<std::vector<CToken>::reverse_iterator> lIds;
        for(auto i=pMatch.mTokens.rbegin()+1;i!=pMatch.mTokens.rend();++i)
        {
            if(i->mToken&TOKENTYPE_WHITESPACE) continue;
            if(i->mToken==TOKEN_OP_DOT)
            {
                if(lLastDot)
                {
                    lIds.empty();
                    break;
                }
                lLastDot=true;
            }
            else if(i->mToken==TOKEN_IDENTIFIER)
            {
                if(!lLastDot) break;
                lIds.push_back(i);
                lLastDot=false;
            }
            else if(i->mToken==TOKEN_RIGHT_SQUARE)
            {
                if(!lLastDot) break;
                if(++i==pMatch.mTokens.rend()) break;
                if(i->mToken!=TOKEN_INTEGER) break;
                auto j=i;
                if(++i==pMatch.mTokens.rend()) break;
                if(i->mToken!=TOKEN_LEFT_SQUARE) break;
                lIds.push_back(j);
            }
            else
            {
                if(lLastDot)
                    lIds.empty();
                break;
            }        
        }

        if(!lIds.empty())
        {
            std::wstring lGroupName=L"Members of ";
            std::wstring lFirstName(lIds.back()->mB,lIds.back()->mE);
            lGroupName+=lFirstName;
            python::YModule lModule=python::get_main_module();
            python::YObject lObject=lModule.GetAttribute(utf8(lFirstName));
            
            if(lObject.IsNull())
            {
                python::error_clear();
                lModule=python::get_builtins_module();
                lObject=lModule.GetAttribute(utf8(lFirstName));
                if(lObject.IsNull())
                    python::error_clear();
            }

            if(!lObject.IsNull())
            {
                for(auto i=lIds.rbegin()+1;i!=lIds.rend();++i)
                {
                    if((*i)->mToken==TOKEN_IDENTIFIER)
                    {
                        std::wstring lName((*i)->mB,(*i)->mE);
                        lGroupName+=L'.';
                        lGroupName+=lName;
                        lObject=lObject.GetAttribute(utf8(lName));
                    }
                    else //TOKEN_INTEGER
                    {
                        std::wstring lIntStr((*i)->mB,(*i)->mE);
                        lGroupName+=L'[';
                        lGroupName+=lIntStr;
                        lGroupName+=L']';
                        python::YSeq lSeq(lObject);
                        lObject=lSeq[to_<int>(lIntStr)];
                    }
                    if(lObject.IsNull())
                    {
                        python::error_clear();
                        break;
                    }
                }
                
                if(!lObject.IsNull())
                {
                    tty::CCompletion<std::wstring>::SGroup lGroup;
                    lGroup.mName=lGroupName;

                    fill_group_with_list(lObject.Dir(),pMatch.mPythonPart,pMatch.mPythonAdd,lGroup);

                    if(!lGroup.mOptions.empty())
                        pComp.mGroups.push_back(std::move(lGroup));
                }
            }
        }
    }
    if(pMatch.mContext&COMP_DOTTEDNAME)
    {
        bool lLastDot=false;
        std::vector<std::vector<CToken>::reverse_iterator> lIds;
        for(auto i=pMatch.mTokens.rbegin()+1;i!=pMatch.mTokens.rend();++i)
        {
            if(i->mToken&TOKENTYPE_WHITESPACE) continue;
            if(i->mToken==TOKEN_OP_DOT)
            {
                if(lLastDot) break;
                lLastDot=true;
            }
            else if(i->mToken==TOKEN_IDENTIFIER)
            {
                if(!lLastDot) break;
                lIds.push_back(i);
                lLastDot=false;
            }
            else
            {
                if(lLastDot)
                    lIds.empty();
                break;
            }        
        }

        tty::CCompletion<std::wstring>::SGroup lGroup;
        lGroup.mName=L"Modules";
        lGroup.mSorted=false;

        if(lIds.empty()) //there is no dot, so complete with all possible modules
        {
            python::YModule lSys=python::import_module("sys");
            python::YList lPath(lSys.GetAttribute("path"));
            
            if(!lPath.IsNull())
            {
                for(int i=0;i<lPath.size();i++)
                {
                    try
                    {
                        std::string lName=lPath[i].ToString();
                        if(lName.empty())
                            lName=".";
                        io::CFolder lFolder(io::file(lName));
                        std::vector<std::string> lNames=lFolder.GetChildrenNames();

                        for(int j=0;j<lNames.size();j++)
                        {
                            if(!starts_with(lNames[j],pMatch.mPythonPart)) continue;
                            
                            std::size_t lDot=lNames[j].rfind('.');
                            if(lDot==std::string::npos) continue;
                            
                            std::string lExtension=lNames[j].substr(lDot+1);
                            
                            if(lExtension!="so"&&
                            lExtension!="py"&&
                            lExtension!="pyc"&&
                            lExtension!="pyo"&&
                            lExtension!="pyd") continue;
                            
                            std::wstring lImport=utf32(lNames[j].substr(0,lDot));
                            lGroup.mOptions.emplace_back(pMatch.mPythonAdd+lImport,lImport,tty::COLOR_WHITE);
                        }
                    }
                    catch(...)
                    {
                    }
                }
            }
            
            python::YSeq lBuiltinModules(lSys.GetAttribute("builtin_module_names"));
            
            if(!lBuiltinModules.IsNull())
            {
                for(int i=0;i<lBuiltinModules.size();i++)
                {
                    std::wstring lName=lBuiltinModules[i].ToWString();
                    
                    if(!starts_with(lName,pMatch.mPythonPart)) continue;
                    lGroup.mOptions.emplace_back(pMatch.mPythonAdd+lName,lName,tty::COLOR_WHITE);
                }
            }
        }

        if(!lGroup.mOptions.empty())
            pComp.mGroups.push_back(std::move(lGroup));
    }
    if(pMatch.mContext&COMP_IMPORT_FROM)
    {
        bool lLastImport=false;
        bool lLastDot=true;
        std::vector<std::vector<CToken>::reverse_iterator> lIds;
        for(auto i=pMatch.mTokens.rbegin()+1;i!=pMatch.mTokens.rend();++i)
        {
            if(i->mToken&TOKENTYPE_WHITESPACE) continue;
            if(!lLastImport)
            {
                if(i->mToken!=TOKEN_KW_IMPORT) break;
                lLastImport=true;
                continue;
            }

            if(i->mToken==TOKEN_OP_DOT)
            {
                if(lLastDot) break;
                lLastDot=true;
            }
            else if(i->mToken==TOKEN_IDENTIFIER)
            {
                if(!lLastDot) break;
                lIds.push_back(i);
                lLastDot=false;
            }
            else
            {
                if(lLastDot)
                    lIds.empty();
                break;
            }        
        }
        
        if(!lIds.empty())
        {
            std::wstring lName(lIds.back()->mB,lIds.front()->mE);
            python::YModule lModule=python::import_module(utf8(lName));
            
            if(lModule)
            {
                tty::CCompletion<std::wstring>::SGroup lGroup;
                lGroup.mName=L"From "+lName;
                lGroup.mSorted=false;
        
                fill_group_with_list(lModule.GetDict().GetKeys(),pMatch.mPythonPart,pMatch.mPythonAdd,lGroup);
                
                if(!lGroup.mOptions.empty())
                    pComp.mGroups.push_back(std::move(lGroup));
            }
            else
            {                
                python::error_clear();
            }
        }
    }
}

void CInterpreter::CompleteFilenames(const std::string &pPrefix,const std::string &pPart,tty::CCompletion<std::wstring>::SGroup &pGroup,
                                io::EGlob pGlobFlags)
{
    std::string lPattern=pPart+"*";

    std::vector<std::string> lGlob;
    io::glob_prefixed(pPrefix,lPattern,io::GLOB_TILDES|io::GLOB_KEEPTILDE|io::GLOB_MARKDIRS|pGlobFlags,lGlob);

    for(int i=0;i<lGlob.size();i++)
    {
        std::wstring lName=utf32(lGlob[i]);
        std::wstring lEscaped=CWord::EscapeBack(lName.begin(),lName.end());
        tty::EColor lColor;
        
        if(lName[lName.length()-1]==L'/')
        {
            lColor=tty::COLOR_BRIGHTBLUE;
        }
        else if(pGlobFlags&io::GLOB_ONLYEXEC)
        {
            lColor=tty::COLOR_BRIGHTGREEN;
            lEscaped+=L' ';
        }
        else
        {
            lColor=tty::COLOR_NORMAL;
            lEscaped+=L' ';
        }
        
        std::size_t lSlash=lName.rfind('/',lName.length()-2);
        
        if(lSlash!=std::string::npos)
            lName.erase(0,lSlash+1);

        pGroup.mOptions.emplace_back(lEscaped,lName,lColor);
    }
}

void CInterpreter::CompleteFilenames(const std::string &pPrefix,const std::string &pPart,tty::CCompletion<std::wstring>::SGroup &pGroup,
                                const std::vector<std::string> &pExt,io::EGlob pGlobFlags)
{
    std::string lPattern=pPart+"*";

    std::vector<std::string> lGlob;
    io::glob_prefixed(pPrefix,lPattern,io::GLOB_TILDES|io::GLOB_KEEPTILDE|io::GLOB_MARKDIRS|pGlobFlags,lGlob);

    for(int i=0;i<lGlob.size();i++)
    {
        if(lGlob[i][lGlob[i].length()-1]!='/')
        {
            bool lFound=false;
            for(int j=0;j<pExt.size();j++)
            {
                if(ends_with(lGlob[i],pExt[j]))
                {
                    lFound=true;
                    break;
                }
            }
            if(!lFound) continue;
        }
        std::wstring lName=utf32(lGlob[i]);
        std::wstring lEscaped=CWord::EscapeBack(lName.begin(),lName.end());
        tty::EColor lColor;
        
        if(lName[lName.length()-1]==L'/')
        {
            lColor=tty::COLOR_BRIGHTBLUE;
        }
        else if(pGlobFlags&io::GLOB_ONLYEXEC)
        {
            lColor=tty::COLOR_BRIGHTGREEN;
            lEscaped+=L' ';
        }
        else
        {
            lColor=tty::COLOR_NORMAL;
            lEscaped+=L' ';
        }
        
        std::size_t lSlash=lName.rfind('/',lName.length()-2);
        
        if(lSlash!=std::string::npos)
            lName.erase(0,lSlash+1);

        pGroup.mOptions.emplace_back(lEscaped,lName,lColor);
    }
}

void CInterpreter::CompleteCommand(const std::string &pPart,tty::CCompletion<std::wstring>::SGroup &pGroup)
{
    pGroup.mName=L"Commands";

    if(pPart.find('/')!=std::string::npos)
    {
        CompleteFilenames("",pPart,pGroup,io::GLOB_ONLYEXEC);
    }
    else
    {
        const std::vector<io::CURI> &lPath=os::get_path();

        for(int i=0;i<lPath.size();i++)
        {
            std::string lPrefix=lPath[i].Path()+'/';
            
            CompleteFilenames(lPrefix,pPart,pGroup,io::GLOB_ONLYEXEC|io::GLOB_NODIR);
        }
        
        //and add local folders
        CompleteFilenames("",pPart,pGroup,io::GLOB_ONLYDIR);
        
        //add builtins
        for(auto i=mBuiltins.begin();i!=mBuiltins.end();++i)
        {
            if(!starts_with(i->first,pPart)) continue;
            std::wstring lName=utf32(i->first);
            pGroup.mOptions.emplace_back(lName+L' ',lName,tty::COLOR_BRIGHTCYAN);
        }
    }

    pGroup.mSorted=false;
}

int CInterpreter::AddJob(PPipeline pJob)
{
    int i;
    
    for(i=0;i<mJobs.size();i++)
    {
        if(!mJobs[i])
            break;
    }
    
    if(i==mJobs.size())
        mJobs.resize(i+1);
    
    mJobs[i]=pJob;
    return i;
}

void CInterpreter::RemoveJob(int pJobID)
{
    mJobs[pJobID].reset();
    
    if(pJobID==mCurrentJob)
    {
        mCurrentJob=-1;
        for(int i=0;i<mJobs.size();i++)
        {
            if(mJobs[i])
            {
                mCurrentJob=i;
                break;
            }
        }
    }
}

void CInterpreter::CheckJobs()
{
    if(buola_pending_sigchld())
    {
        while(CProcess *lProcess=CProcess::CheckOne())
        {
            CPipeline *lPipeline=CPipeline::GetPipeline(lProcess);
            if(lPipeline)
                lPipeline->Check();
        }
    }
    
    if(mPendingJobReport)
    {
        msg_info() << "\n";
        
        for(int i=0;i<mJobs.size();i++)
        {
            if(mJobs[i])
                mJobs[i]->PrintStatus(true,msg_info());
        }
        
        mPendingJobReport=false;
    }
}

/*namespace bush*/ } /*namespace buola*/ }
